package com.chenqq.camerademo.camera.usb;

import android.hardware.usb.UsbDevice;
import android.hardware.usb.UsbDeviceConnection;
import android.hardware.usb.UsbInterface;
import android.os.Build;
import android.text.TextUtils;
import android.util.Log;
import android.util.SparseArray;

import com.chenqq.camerademo.camera.bean.UsbDeviceInfo;
import com.chenqq.camerademo.camera.helper.UsbHelperTools;

import java.lang.ref.WeakReference;
import java.util.Locale;

import androidx.annotation.RequiresApi;

public class UsbControlBlock implements Cloneable{
    private String Tag=this.getClass().getSimpleName();
    private final WeakReference<UsbDevice> mWeakDevice;
    protected UsbDeviceConnection mConnection;
    protected final UsbDeviceInfo mInfo;
    private final int mBusNum;
    private final int mDevNum;
    private final SparseArray<SparseArray<UsbInterface>> mInterfaces = new SparseArray<SparseArray<UsbInterface>>();

    /**
     * this class needs permission to access USB device before constructing

     * @param device
     */
    public UsbControlBlock( UsbDevice device) {
        mWeakDevice = new WeakReference<UsbDevice>(device);
        mConnection = UsbHelperTools.getInStance().getUsbManager().openDevice(device);
        mInfo = UsbHelperTools.getInStance().initDeviceInfo(device);
        final String name = device.getDeviceName();
        final String[] v = !TextUtils.isEmpty(name) ? name.split("/") : null;
        int busnum = 0;
        int devnum = 0;
        if (v != null) {
            busnum = Integer.parseInt(v[v.length-2]);
            devnum = Integer.parseInt(v[v.length-1]);
        }
        mBusNum = busnum;
        mDevNum = devnum;
        if (mConnection != null) {
            final int desc = mConnection.getFileDescriptor();
            final byte[] rawDesc = mConnection.getRawDescriptors();
            Log.i(Tag, String.format(Locale.US, "name=%s,desc=%d,busnum=%d,devnum=%d,rawDesc=", name, desc, busnum, devnum) + rawDesc);
        } else {
            Log.e(Tag, "could not connect to device " + name);
        }
    }
    /**
     * copy constructor
     * @param src
     * @throws IllegalStateException
     */

    public UsbControlBlock( UsbControlBlock src) throws IllegalStateException {
        final UsbDevice device = src.getDevice();
        if (device == null) {
            throw new IllegalStateException("device may already be removed");
        }
        mConnection = UsbHelperTools.getInStance().getUsbManager().openDevice(device);
        if (mConnection == null) {
            throw new IllegalStateException("device may already be removed or have no permission");
        }
        mInfo = UsbHelperTools.getInStance().initDeviceInfo(device);
        mWeakDevice = new WeakReference<UsbDevice>(device);
        mBusNum = src.mBusNum;
        mDevNum = src.mDevNum;
    }

    @Override
    public UsbControlBlock clone() throws CloneNotSupportedException {
        final UsbControlBlock ctrlblock;
        try {
            ctrlblock = new UsbControlBlock(this);
        } catch (final IllegalStateException e) {
            throw new CloneNotSupportedException(e.getMessage());
        }
        return ctrlblock;
    }

    public final UsbDevice getDevice() {
        return mWeakDevice.get();
    }

    /**
     * get device name
     * @return
     */
    public String getDeviceName() {
        final UsbDevice device = mWeakDevice.get();
        return device != null ? device.getDeviceName() : "";
    }

    /**
     * get device id
     * @return
     */
    public int getDeviceId() {
        final UsbDevice device = mWeakDevice.get();
        return device != null ? device.getDeviceId() : 0;
    }

    /**
     * get vendor id
     * @return
     */
    public int getVenderId() {
        final UsbDevice device = mWeakDevice.get();
        return device != null ? device.getVendorId() : 0;
    }

    /**
     * get product id
     * @return
     */
    public int getProductId() {
        final UsbDevice device = mWeakDevice.get();
        return device != null ? device.getProductId() : 0;
    }

    /**
     * get version
     * @return
     */
    public String getVersion() {
        return mInfo.version;
    }

    public int getBusNum() {
        return mBusNum;
    }

    public int getDevNum() {
        return mDevNum;
    }

    @Override
    public boolean equals(final Object o) {
        if (o == null) return false;
        if (o instanceof UsbControlBlock) {
            final UsbDevice device = ((UsbControlBlock) o).getDevice();
            return device == null ? mWeakDevice.get() == null
                    : device.equals(mWeakDevice.get());
        } else if (o instanceof UsbDevice) {
            return o.equals(mWeakDevice.get());
        }
        return super.equals(o);
    }

    /**
     * get file descriptor to access USB device
     * @return
     * @throws IllegalStateException
     */
    public synchronized int getFileDescriptor() throws IllegalStateException {
        checkConnection();
        return mConnection.getFileDescriptor();
    }

    /**
     * get interface
     * @param interface_id
     * @param altsetting
     * @return
     * @throws IllegalStateException
     */
    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
    public synchronized UsbInterface getInterface(final int interface_id, final int altsetting) throws IllegalStateException {
        checkConnection();
        SparseArray<UsbInterface> intfs = mInterfaces.get(interface_id);
        if (intfs == null) {
            intfs = new SparseArray<UsbInterface>();
            mInterfaces.put(interface_id, intfs);
        }
        UsbInterface intf = intfs.get(altsetting);
        if (intf == null) {
            final UsbDevice device = mWeakDevice.get();
            final int n = device.getInterfaceCount();
            for (int i = 0; i < n; i++) {
                final UsbInterface temp = device.getInterface(i);
                if ((temp.getId() == interface_id) && (temp.getAlternateSetting() == altsetting)) {
                    intf = temp;
                    break;
                }
            }
            if (intf != null) {
                intfs.append(altsetting, intf);
            }
        }
        return intf;
    }
    private synchronized void checkConnection() throws IllegalStateException {
        if (mConnection == null) {
            throw new IllegalStateException("already closed");
        }
    }
    /**
     * 关闭
     */
    public synchronized void close() {
        if (mConnection != null) {
            final int n = mInterfaces.size();
            for (int i = 0; i < n; i++) {
                final SparseArray<UsbInterface> intfs = mInterfaces.valueAt(i);
                if (intfs != null) {
                    final int m = intfs.size();
                    for (int j = 0; j < m; j++) {
                        final UsbInterface intf = intfs.valueAt(j);
                        mConnection.releaseInterface(intf);
                    }
                    intfs.clear();
                }
            }
            mInterfaces.clear();
            mConnection.close();
            mConnection = null;
        }
    }

}
